# encoding: utf-8
require 'yaml'
require 'json'
require 'time'
require 'stalker'

# status workflow
SW = {
	:new => [:acknowledged, :rejected, :assigned],
	:rejected => [:closed, :new],
	:acknowledged => [:assigned, :resolved],
	:assigned => [:resolved],
	:resolved => [:assigned, :closed],
	:closed => [:reopen]
}

# AppController
class AppController < Sinatra::Base
	include MatrixHelpers::APP

	set :server, %w[thin mongrel webrick]
	set :port, 9090
	set :static, true
	set :method_override, true
	enable :sessions

	@@websites = File.exists?('config/websites.yml') ? YAML.load_file('config/websites.yml') : YAML.load_file('config/websites.yml.default')

	# Settings
	ConfigDB.first_or_create({:name => 'environment'}, {:value => ENV['RACK_ENV'] || 'production'})
	ConfigDB.first_or_create({:name => 'auth_method'}, {:value => ENV['AUTH'] || 'passport'})
	ConfigDB.first_or_create({:name => 'message_queue'}, {:value => ENV['MQ'] || ''})

	# Authentication
	@@auth = ConfigDB.first :name => 'auth_method'
	auth_method = @@auth.value
	auth_mod = File.expand_path("./lib/auth_#{auth_method}", File.dirname(__FILE__))
	require auth_mod
	include Auth

	# message_queue
	@@mq = ConfigDB.first :name => 'message_queue'

	# system user: root, guest
	UserGroup.first_or_create({:id => 1}, {:name => 'Administrators', :description => '管理员'})
	UserGroup.first_or_create({:id => 2}, {:name => 'User', :description => '用户'})
	UserGroup.first_or_create({:id => 3}, {:name => 'Anonymous', :description => '匿名用户'})
	User.first_or_create({:name => 'root'}, {:user_group_id => 1})
	User.first_or_create({:name => 'guest'}, {:user_group_id => 3})
	Project.first_or_create({:id => 1}, {:name => '未分类'})
	Project.first_or_create({:id => 2}, {:name => 'Matrix优化'})
	Project.first_or_create({:id => 3}, {:name => '新技术评测'})
	Tasklet.first_or_create({:name => 'Welcome'}, { :description => '-', :user_id => 1, :project_id => 1, :due => Time.now + 86400})
	Tasklet.first_or_create({:name => 'Feedback'}, { :description => 'write feedback on https://github.com/Lax/matrix', :user_id => 1, :project_id => 1, :due => Time.now + 86400})
	Trace.first_or_create({:tasklet_id => Tasklet.first.id, :name => '任务执行记录1'})
	Trace.first_or_create({:tasklet_id => Tasklet.first.id, :name => '预设检查点1', :check_point => true}, {:created_at => Time.now - 86400, :due => Time.now + 86400})
	Trace.first_or_create({:tasklet_id => Tasklet.first.id, :name => '预设检查点2', :check_point => true}, {:created_at => Time.now - 86400, :due => Time.now - 86400/2, :finished_at => Time.now - 86400/4})
	Trace.first_or_create({:tasklet_id => Tasklet.first.id, :name => '任务执行记录2'})
	Trace.first_or_create({:tasklet_id => Tasklet.first.id, :name => '预设检查点3', :check_point => true}, {:created_at => Time.now - 86400, :due => Time.now - 86400/2})

	before do
		@current_user = auth_login if @@auth
		@projects = Project.all
		@users = User.all
		@matrix_root = ENV['MATRIX_ROOT'] || 'https://uimatrix.herokuapp.com'
		@tasklet_status = "new/rejected/acknowledged/assigned/resolved/closed".split('/')
		@current_panel = "Index"
	end

	["/tasks", "/tasklets", "/project/:project_id"].each do |loc|
		get loc do
			#selector = { :order => [:finished_at.desc, :urgent.desc, :important.desc, :created_at.desc, :id.desc] }
			selector = { :order => [:id.desc] }
			if params[:project_id] and @current_project = Project.get(params[:project_id])
				selector[:project_id] = @current_project.id
				@current_panel = "project_#{@current_project.id}"
			end
			@tasks = Tasklet.all(selector)

			if params[:fmt] && params[:fmt] == "yaml"
				content_type 'text/plain'
				@tasks.reverse.to_yaml
			elsif params[:fmt] && params[:fmt] == 'json'
				@tasks.map do |task|
					{ "name" => task.name,
					"desc" => task.status,
					"values" => [{'from' => "/Date(#{Time.parse(task.created_at.asctime).to_i * 1000})/",
							'to' => "/Date(#{Time.parse(task.finished? ? task.updated_at.asctime : task.due.asctime).to_i * 1000})/",
							'desc' => "[#{task.name}] #{task.description}"
						}]
					}
				end.to_json
			else
				if params[:gantt] and params[:gantt] == 'true'
					erb :tasklet_gantt, :layout => !request.xhr?
				else
					erb :tasklet_list
				end
			end
		end
	end

	["/", "/myview", "/myview/:status"].each do |loc|
		get loc do
			@current_panel = "MyView"
			@current_point = params[:status] ? "MyView_#{params[:status]}" : "MyView_all"

			order = [:finished_at.desc, :urgent.desc, :important.desc, :created_at.desc, :id.desc]

			# myviews: unassigned, :assign_to_me, :created_by_me, :monitor_by_me, :recent_update => []
			@myviews = {}

			# init __selector
			__selector = {
				:assign_to_me => {:assignment => {:user => @current_user}},
				:unassigned => {:assignment => nil},
				:created_by_me => {:user => @current_user},
				:monitor_by_me => {:monitors => {:user => @current_user}}
			}
			__selector.keys.each do |list|
				__selector[list][:order] = [:created_at.desc]

				__selector[list][:limit] = 30 unless params[:status]
				__selector[list][:status] = :closed if status = params[:status] and status.to_sym == :closed
				__selector[list][:status.not] = :closed if status = params[:status] and status.to_sym == :open
			end

			# get tasklets
			__selector.keys.each do |list|
				@myviews[list] = Tasklet.all(__selector[list])
			end

			erb :myview
		end
	end

	["/kanban"].each do |loc|
		get loc do
			@current_panel = "Kanban"

			@kanban = {}

			@kanban[:new] = Tasklet.all(:status => :new, :order => [:created_at.desc]) + Tasklet.all(:status => :reopen, :order => [:created_at.desc]) + Tasklet.all(:status => :acknowledged, :order => [:created_at.desc])
			@kanban[:doing] = Tasklet.all(:status => :assigned, :order => [:created_at.desc])
			@kanban[:review] = Tasklet.all(:status => :resolved, :order => [:created_at.desc]) + Tasklet.all(:status => :rejected, :order => [:created_at.desc])

			erb :kanban
		end
	end

	["/task/create", "/tasklet/create"].each do |loc|
		post loc do
			task = Tasklet.new(params)
			if task.name != "" and task.save
				Monitor.first_or_create(:tasklet_id => task.id, :user_id => task.user_id)
				project = Project.get(task.project_id)
				redirect task.link
			else
				redirect "#{project.link}?msg=creation_failed_of_tasklet"
			end
		end
	end

	["/task/import", "/tasklet/import"].each do |loc|
		get loc do
			erb :tasklet_import
		end

		post loc do
			if params[:format] == 'yaml' and params[:content]
				entries = YAML.load(params[:content])
				entries.each do |entry|
					entry['id'] = nil
					Tasklet.new(entry).save
				end
				redirect '/'
			end
			redirect '/task/import?msg=import_failed_of_tasklet'
		end
	end

	["/task/:id", "/tasklet/:id"].each do |loc|
		get loc do
			@task = Tasklet.get(params[:id])
			@tasklet_next_status = SW[@task.status.to_sym] || @tasklet_status
			@traces = Trace.all(:tasklet_id => @task.id, :order => [:id.desc])

			@current_panel = "project_#{@task.project_id}"
			erb :tasklet_show, :layout => !request.xhr?
		end

		put loc do
			exec_result = nil
			msg = nil
			task = Tasklet.get(params[:id])

			if params[:status] and task.status != params[:status]
				status_before = task.status
				status_after = params[:status]
				msg = "状态更新: #{status_before} => #{status_after}"
				task.status = params[:status]
				exec_result = true if task.save

			elsif params[:monitor_add] and params[:user_id]
				user = User.get(params[:user_id])
				if ! Monitor.first(:tasklet_id => task.id, :user_id => user.id) and Monitor.create(:tasklet_id => task.id, :user_id => user.id)
					msg = "关注者添加 #{user.name}"
					exec_result = true
				end

			elsif params[:monitor_del] and params[:user_id]
				user = User.get(params[:user_id])
				if m = Monitor.get(:tasklet_id => task.id, :user_id => user.id)
					m.destroy
					msg = "关注者删除 #{user.name}"
				end
				exec_result = true


			elsif params[:assign_to]
				if !task.assign_to or task.assign_to.id != User.get(params[:assign_to]).id
					assignment = Assignment.first_or_create(:tasklet_id => task.id)

					user_before = assignment.user ? assignment.user.name : '-'
					user_after = User.get(params[:assign_to]).name
					assignment.user_id = params[:assign_to]

					if assignment.save
						exec_result = true
						task.update(:status => 'assigned') if task.status == 'new' or task.status == 'reopen'
						Monitor.first_or_create(:tasklet_id => task.id, :user_id => assignment.user_id)
						msg = "分配: #{user_before} => #{user_after}"
					end
				end
			end

			if exec_result
				Trace.create(:tasklet_id => task.id, :user_id => @current_user.id, :name => msg) if msg
				task.monitors.user.each do |u|
					Stalker.enqueue('email.send',
						:from => @current_user.name,
						:to => u.name,
						:ref => "tasklet_#{task.id}@todo.som",
						:subject => "[TODO #{task.id}] #{task.name}",
						:body => %|[UPDATE][#{Time.now.ctime}] #{msg}

--------------------
标题： #{task.name}
描述： #{task.description}

创建日期： #{task.created_at.ctime}  任务创建者： #{task.user.name}
期望日期： #{task.due.ctime}  当前执行者： #{task.assign_to ? task.assign_to.name : '-'}

--------------------
#{ Trace.all(:tasklet_id => task.id).map {|tt| "[#{tt.created_at.ctime}] #{tt.user.name} - #{tt.name}\n#{tt.description}\n"}.join("\n") }

--------------------
查看详情: #{@matrix_root}#{task.link}?from=send.mail

|
					) if @@mq.value == 'stalker'
				end

				status 201
				redirect task.link
			else
				status 412
				redirect "#{task.link}?update_failed_of_tasklet"
			end
		end

		#delete loc do
		#	Task.get(params[:id]).destroy
		#	redirect '/'
		#end
	end

	['/trace/import'].each do |loc|
		get loc do
			erb :trace_import
		end

		post loc do
			if params[:format] == 'yaml' and params[:content]
				entries = YAML.load(params[:content])
				entries.each do |entry|
					entry['id'] = nil
					Trace.new(entry).save
				end
				redirect '/'
			end
			redirect '/?import_failed_of_trace'
		end
	end

	get '/traces' do
		traces = Trace.all

		content_type 'text/plain'
		traces.to_yaml
	end

	post '/trace/create' do
		trace = Trace.new(params)
		msg = trace.name

		if trace.save
			task = trace.tasklet
			task.monitors.user.each do |u|
				Stalker.enqueue('email.send',
					:from => @current_user.name,
					:to => u.name,
					:ref => "tasklet_#{task.id}@todo.som",
					:subject => "[TODO #{task.id}] #{task.name}",
					:body => %|[UPDATE][#{Time.now.ctime}] #{msg}

--------------------
标题： #{task.name}
描述： #{task.description}

创建日期： #{task.created_at.ctime}  任务创建者： #{task.user.name}
期望日期： #{task.due.ctime}  当前执行者： #{task.assign_to ? task.assign_to.name : '-'}

--------------------
#{ Trace.all(:tasklet_id => task.id).map {|tt| "[#{tt.created_at.ctime}] #{tt.user.name} - #{tt.name}\n#{tt.description}\n"}.join("\n") }

--------------------
查看详情: #{@matrix_root}#{task.link}?from=send.mail

|
				) if @@mq.value == 'stalker'
			end

			redirect Tasklet.get(trace.tasklet_id).link

		else
			redirect '/?creation_failed_of_trace'

		end
	end

	put '/trace/:id' do
		trace = Trace.get(params[:id])
		trace.finished_at = params[:finished] ? Time.now : nil
		trace.user_id = params[:user_id] ? params[:user_id] : trace.user_id
		if trace.save
			status 201
			redirect Tasklet.get(trace.tasklet_id).link
		else
			status 412
			redirect '/?update failed_of_trace'
		end
	end

	get '/user/logout' do
		session[:username] = nil
		redirect '/'
	end

	get '/user/:id' do
		@user = User.get(params[:id])
		@tasklets = Tasklet.all({:status.not => :closed, :assignment => {:user => @user}})

		erb :user_show
	end

	get '/matrix/config' do
		@settings = ConfigDB.all
		@user_groups = UserGroup.all
		erb :matrix_config
	end

	["/websites"].each do |loc|
		get loc do
			@current_panel = "websites"
			@websites = @@websites
			erb :websites
		end
	end

	register Sinatra::Async
	@@pool = Hash.new []

	get '/events' do
		@current_panel = "events"
		@channel = params[:channel] || 'default'
		erb :events
	end

	aget '/events/sub/:channel' do
		@@pool[params[:channel]].push self
	end

	aget '/events/pub/:channel' do
		msg = params[:msg] ? params[:msg] : '-'
		@@pool[params[:channel]].each {|observer| observer.body { msg } }
		@@pool[params[:channel]] = []
		body "sent!"
	end
end
